#include <linux/uaccess.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>

/*****************************************************************************
 *  Include Section
 *  add all #include here
 *****************************************************************************/
#include <linux/mol_efuse.h>
#include <mach/mol_efuse_plat.h>
#include <linux/io.h>
#include <linux/of_platform.h>
#include <linux/of_address.h>
#include <linux/pm.h>
/*****************************************************************************
 * Define section
 * add all #define here
 *****************************************************************************/
#define wrap_readl(wrap, name) \
	__raw_readl(&(((struct wrap_efuse_reg *)wrap->regs)->name))

#define wrap_writel(wrap, name, val) \
	__raw_writel((val), &(((struct wrap_efuse_reg *)wrap->regs)->name))

#define wrap_readw(wrap, name) \
	__raw_readw(&(((struct wrap_efuse_reg *)wrap->regs)->name))

#define wrap_writew(wrap, name, val) \
	__raw_writew((val), &(((struct wrap_efuse_reg *)wrap->regs)->name))

#define wrap_readb(wrap, name) \
	__raw_readb(&(((struct wrap_efuse_reg *)wrap->regs)->name))

#define wrap_writeb(wrap, name, val) \
	__raw_writeb((val), &(((struct wrap_efuse_reg *)wrap->regs)->name))

#define MOL_EFUSE_PLAT_DEVICE_NAME			"mol_efuse"
#define MOL_EFUSE_MISC_DEVICE_NAME			"fh_efuse"
#define MOL_EFUSE_MISC_DEVICE_NODE_NAME		"mol_efuse_node"
#define EFUSE_HW_BUFFER_POS					(4)

#if defined CONFIG_ARCH_FH8632 || defined CONFIG_ARCH_FH8626V100
#define MATCH_KEY 0x53d485a7
#else
#define MATCH_KEY 0x92fc0025
#endif

#define SECURE_BOOT_ENTRY_V64       (255)
#define SECURE_BOOT_ENTRY_V16       (48)
#define SECURE_BOOT_ACTIVE_VAL  (1<<0)
#define SOC_BOOT_MODE_WITH_SECURE   0xaa5555aa
#define SECURE_PROTECT_EFUSE_ENTRY_MAX_INDEX    16
/****************************************************************************
 * ADT section
 *  add definition of user defined Data Type
 *  that only be used in this file  here
 ***************************************************************************/

struct wrap_efuse_reg {
	u32 efuse_cmd;				/*0x0*/
	u32 efuse_config;			/*0x4*/
	u32 efuse_match_key;		/*0x8*/
	u32 efuse_timing0;			/*0xc*/
	u32 efuse_timing1;			/*0x10*/
	u32 efuse_timing2;			/*0x14*/
	u32 efuse_timing3;			/*0x18*/
	u32 efuse_timing4;			/*0x1c*/
	u32 efuse_timing5;			/*0x20*/
	u32 efuse_timing6;			/*0x24*/
	u32 efuse_dout;				/*0x28*/
	u32 efuse_status0;			/*0x2c*/
	u32 efuse_status1;			/*0x30*/
	u32 efuse_status2;			/*0x34*/
	u32 efuse_status3;			/*0x38*/
	u32 efuse_status4;			/*0x3c*/
    u32 efuse_write_val;        /* 0x40 */
    u32 efuse_status6;          /* 0x44 */
    u32 efuse_status7;          /* 0x48 */
    u32 efuse_status8;          /* 0x4c */
};


enum {
    CMD_WFLGA = 0,
    CMD_LOAD_USR_CMD = 1,
    CMD_WRITE = 2,
    CMD_READ = 3,
	CMD_TRANS_AESKEY = 4,
	CMD_WFLGA_AUTO = 8,
	CMD_CMP = 15,
};

enum {
    EFUSE_TRANS_TO_AES_WIDTH_32 = 3,
	EFUSE_TRANS_TO_AES_WIDTH_64 = 7,
	EFUSE_TRANS_TO_AES_WIDTH_128 = 15,
	EFUSE_TRANS_TO_AES_WIDTH_192 = 23,
	EFUSE_TRANS_TO_AES_WIDTH_256 = 31,
};

struct wrap_efuse_obj s_efuse_obj = {0};

/* cmd bit mask*/
#define EFUSE_CMD_OPC_POS       0
#define EFUSE_CMD_ADDR_POS      4
#define EFUSE_CMD_BIT_WRFLAG_POS     12
#define EFUSE_CMD_AES_KEYID_POS      20

#define EFUSE_VALIDITY_OK   0
#define EFUSE_VALIDITY_ERR   1

#define ENTRY_ALLIGN        1
#define ENTRY_NOT_ALLIGN        0

/* Molchip efuse ctl entry map (64 block version)*/
/****************************************/
/*NAME       ENTREY         ENTRY_NO_2_BLK      COMMENT

/*NORMAL     (000 ~ 223)    BLK00 ~ BLK55       USR AREA    total 56 BLK*/
/*SOC_DEF    (224 ~ 227)    BLK56               BONDING AREA    */
/*TEST       (228 ~ 243)    BLK57 ~ BLK60       CAN'T LK    */
/*LK_W_EFUSE (244 ~ 251)    BLK61 ~ BLK62       BIT[0] : BLK[0] ... BIT[56] : BLK[56], {else :: BIT[57 - 63] : REV }*/
/*LK_R_APB   (252 ~ 253)    BLK63               BIT[0] : BLK[0-3] ..BIT[13] : BLK[52-55], BIT[14] : BLK[56], {else :: BIT[15] : REV}*/
/*LK_R_AHB   (254      )    BLK63               BIT[0] : KEY[0-1] ..BIT[3] : KEY[3 : 7], {else :: BIT[4 - 7] : REV}*/
/*RESERVE    (255      )    BLK63        */
/****************************************/


/* Molchip efuse ctl entry map (16 block version)*/
/****************************************/
/*NAME       ENTREY         ENTRY_NO_2_BLK      COMMENT

/*NORMAL     (00 ~ 47)      BLK00 ~ BLK11       USR AREA    total 12 BLK*/
/*TEST       (48 ~ 58)      BLK12 ~ BLK14       CAN'T LK    BLK12-ENTRY51-BONDING AREA*/
/*RESERVE    (59)           BLK14                                                                                  */
/*LK_R_APB   (60)           BLK15               BIT[0] : BLK[0-3] ..BIT[2] : BLK[8-11], {else :: BIT[3 - 7] : REV}*/
/*LK_W_EFUSE (61 ~ 62)      BLK15               BIT[0] : BLK[0] ... BIT[11] : BLK[11], {else :: BIT[12 - 15] : REV }*/
/*LK_R_AHB   (63)           BLK15               BIT[0] : KEY[0-1] ..BIT[3] : KEY[6 : 7], {else :: BIT[4 - 7] : REV}*/
/****************************************/




/******************************************************************************
 * Function prototype section
 * add prototypes for all functions called by this file,execepting those
 * declared in header file
 *****************************************************************************/

/*****************************************************************************
 * Global variables section - Exported
 * add declaration of global variables that will be exported here
 * e.g.
 *  int8_t foo;
 ****************************************************************************/

/*****************************************************************************

 *  static fun;
 *****************************************************************************/
static int mol_efuse_open(struct inode *inode, struct file *file);
static int mol_efuse_release(struct inode *inode, struct file *filp);
static long mol_efuse_ioctl(struct file *filp, unsigned int cmd,
		unsigned long arg);
/*****************************************************************************
 * Global variables section - Local
 * define global variables(will be refered only in this file) here,
 * static keyword should be used to limit scope of local variable to this file
 * e.g.
 *  static uint8_t ufoo;
 *****************************************************************************/

static const struct file_operations mol_efuse_fops = {
		.owner = THIS_MODULE,
		.open = mol_efuse_open,
		.release = mol_efuse_release,
		.unlocked_ioctl = mol_efuse_ioctl,
};

static struct miscdevice mol_efuse_misc = {
	.minor = MISC_DYNAMIC_MINOR,
	.name = MOL_EFUSE_MISC_DEVICE_NAME,
	/*.nodename = MOL_EFUSE_MISC_DEVICE_NODE_NAME,*/
	.fops = &mol_efuse_fops,
};

static int mol_efuse_open(struct inode *inode, struct file *file)
{
	return 0;
}

static int mol_efuse_release(struct inode *inode, struct file *filp)
{
	return 0;
}



static int efuse_detect_complete(struct wrap_efuse_obj *obj, int pos)
{
	unsigned int rdata;
	unsigned int time = 0;
	/*printk("efuse wait pos %x...\n",pos);*/
	do {
		time++;
		rdata = wrap_readl(obj, efuse_status0);
		if (time > 1000) {
			printk("[efuse]:detect time out...pos: 0x%x\n", pos);
			return -1;
		}
		udelay(10);
	} while ((rdata&(1<<pos)) != 1<<pos);
    /*printk("efuse wait pos done...\n",pos);*/
	udelay(10);
	return 0;
}

static int efuse_write_pre_check(struct wrap_efuse_obj *obj, u32 entry)
{
    int blk_no;
    u32 data;

    blk_no = entry / 4;
    data =  (CMD_WFLGA << EFUSE_CMD_OPC_POS) | (blk_no << EFUSE_CMD_ADDR_POS);
    wrap_writel(obj, efuse_cmd, data);
    return efuse_detect_complete(obj, CMD_WFLGA);
}

static void efuse_write_key_bit_to_1(struct wrap_efuse_obj *obj, u32 entry, u8 pos)
{
    int blk_no;
    u32 data;
 
    blk_no = entry / 4;
    efuse_write_pre_check(obj, entry);

    wrap_writel(obj, efuse_write_val, (pos + (8 * (entry % 4))));
    data = (CMD_WRITE << EFUSE_CMD_OPC_POS) |
    	(blk_no << EFUSE_CMD_ADDR_POS) |
    	(0xff << EFUSE_CMD_BIT_WRFLAG_POS);
    wrap_writel(obj, efuse_cmd, data);
    efuse_detect_complete(obj, CMD_WRITE);
}

static void efuse_write_key_word(struct wrap_efuse_obj *obj, u32 entry, u32 wdata)
{
    int blk_no;
    u32 data;

    blk_no = entry / 4;
    efuse_write_pre_check(obj, entry);

    wrap_writel(obj, efuse_write_val, wdata);
    data = (CMD_WRITE << EFUSE_CMD_OPC_POS) |
    	(blk_no << EFUSE_CMD_ADDR_POS);
    wrap_writel(obj, efuse_cmd, data);
    efuse_detect_complete(obj, CMD_WRITE);
}


void efuse_write_key_byte(struct wrap_efuse_obj *obj, u32 entry, u8 data)
{
    int i;

    for(i = 0; i < 8; i++){
        if(data & (1 << i))
            efuse_write_key_bit_to_1(obj, entry, i);
    }
}

static u32 efuse_read_entry_word(struct wrap_efuse_obj *obj, u32 entry_no)
{
    int blk_no;
    u32 data;

    blk_no = entry_no / 4;
    data = (CMD_READ << EFUSE_CMD_OPC_POS) | (blk_no << EFUSE_CMD_ADDR_POS);
    wrap_writel(obj, efuse_cmd, data);
    efuse_detect_complete(obj, CMD_READ);

    return wrap_readl(obj, efuse_status4);
}

static u8 efuse_read_entry_byte(struct wrap_efuse_obj *obj, u32 entry_no)
{
    int blk_no;
    u32 data;

    blk_no = entry_no / 4;
    data = (CMD_READ << EFUSE_CMD_OPC_POS) | (blk_no << EFUSE_CMD_ADDR_POS);
    wrap_writel(obj, efuse_cmd, data);
    efuse_detect_complete(obj, CMD_READ);
    data = wrap_readl(obj, efuse_status4);
    data = data >> (8 * (entry_no % 4));

    return (u8)data;
}

static int efuse_check_write_validity(struct wrap_efuse_obj *obj)
{
    u32 ret; 

    ret = wrap_readl(obj, efuse_status3);
    if(ret & (1 << 7))
        return EFUSE_VALIDITY_ERR;

    return  EFUSE_VALIDITY_OK;
}

static int efuse_check_read_validity(struct wrap_efuse_obj *obj)
{
    u32 ret; 

    ret = wrap_readl(obj, efuse_status3);
    if(ret & (1 << 8))
        return EFUSE_VALIDITY_ERR;

    return  EFUSE_VALIDITY_OK;
}

static int check_entry_4byte_allign(u32 start_entry, u32 size)
{
    if((start_entry % 4 == 0) && (size % 4 == 0))
        return ENTRY_ALLIGN;
    else
        return ENTRY_NOT_ALLIGN;
}

void efuse_write_entry(struct wrap_efuse_obj *obj, u32 start_entry, u8 *buff, u32 size)
{
    u32 data, i;

    if (efuse_check_write_validity(obj) != EFUSE_VALIDITY_OK) {
        pr_err("efuse hw not allow to write efuse.\n");
        return;
    }

    if (check_entry_4byte_allign(start_entry, size)) {
        /* word process */
        for (i = 0; i < size; i += 4) {
            memcpy(&data, buff, 4);
            efuse_write_key_word(obj, start_entry + i, data);
            buff += 4;
        }
    } else {
        /* byte process */
        for (i = 0; i < size; i++) {
            efuse_write_key_byte(obj, start_entry + i, *buff++);
        }
    }

}

void efuse_read_entry(struct wrap_efuse_obj *obj, u32 key,
	u32 start_entry, u8 *buff, u32 size)
{
    u32 data, i;

    wrap_writel(obj, efuse_match_key, key);
    if (check_entry_4byte_allign(start_entry, size)) {
        /* word process */
        for (i = 0; i < size; i += 4) {
            data = efuse_read_entry_word(obj, start_entry + i);
            memcpy(buff, &data, 4);
            buff += 4;
        }

    } else {
        /* byte process */
        for (i = 0; i < size; i++) {
            data = efuse_read_entry_byte(obj, start_entry + i);
            *buff++ = (u8) data;
        }
    }
}

static void auto_check_efuse_pro_bits(struct wrap_efuse_obj *obj,
	struct efuse_status *status)
{
	if (obj->total_blocks == EFUSE_64BLK_VER) {
		/*
		 * [bit0 : bit31]: block0 ~ block31
		 * [bit32 : bit63]: block32 ~ block63
		 */
		status->protect_bits[0] = wrap_readl(obj, efuse_status1);
		status->protect_bits[1] = wrap_readl(obj, efuse_status2);
	} else {
		/*
		 * [bit0 : bit15]: block0 ~ block15
		 */
		status->protect_bits[0] = wrap_readl(obj, efuse_status1);
		status->protect_bits[0] &= 0xFFFF;
		status->protect_bits[1] = 0;
	}
}

static void efuse_get_lock_status(struct wrap_efuse_obj *obj,
	struct efuse_status *status)
{
	if (obj->total_blocks == EFUSE_64BLK_VER) {
		/*
		 * bit0: block0 ~ block3 locked
		 * bit1: block4 ~ block7 locked
		 * ...
		 * bit14: block56 ~ block59 locked
		 */
		status->efuse_apb_lock = wrap_readl(obj, efuse_status6) & 0x7fff;
		/*
		 * bit24: aes key0 & key1 locked
		 * bit25: aes key2 & key3 locked
		 * bit26: aes key4 & key5 locked
		 * bit27: aes key6 & key7 locked
		 */
		status->aes_ahb_lock = (wrap_readl(obj, efuse_status0) >> 24) & 0xf;
	} else {
		/*
		 * bit0: block0 ~ block3 locked
		 * bit1: block4 ~ block7 locked
		 * bit2: block8 ~ block11 locked
		 */
		status->efuse_apb_lock = wrap_readl(obj, efuse_status6) & 0x7;
		/*
		 * bit24: aes key0 & key1 locked
		 * bit25: aes key2 & key3 locked
		 * bit26: aes key4 & key5 locked
		 * bit27: aes key6 & key7 locked
		 */
		status->aes_ahb_lock = (wrap_readl(obj, efuse_status0) >> 24) & 0xf;
	}
}

static void efuse_set_read_lock(struct wrap_efuse_obj *obj,
	struct efuse_status *status)
{
	u32 data;

	if (obj->total_blocks == EFUSE_64BLK_VER) {
		/*
		 * bit0: lock aes key0 & key1
		 * bit1: lock aes key2 & key3
		 * bit2: lock aes key4 & key5
		 * bit3: lock aes key6 & key7
		 */
	    data = status->aes_ahb_lock;
	    data &= 0x0f;
	    efuse_write_key_byte(&s_efuse_obj, 254, (u8)data);

		/*
		 * bit0: lock block0 ~ block3
		 * bit1: lock block4 ~ block7
		 * ...
		 * bit14: lock block56 ~ block59
		 */
	    data = status->efuse_apb_lock;
	    data &= 0x7fff;
	    //set low 8bit
	    efuse_write_key_byte(&s_efuse_obj, 252, (u8) data);
	    //set high 8bit
	    efuse_write_key_byte(&s_efuse_obj, 253, (u8)(data >> 8));
	} else {
		/*
		 * bit0: lock aes key0 & key1
		 * bit1: lock aes key2 & key3
		 * bit2: lock aes key4 & key5
		 * bit3: lock aes key6 & key7
		 */
		data = status->aes_ahb_lock;
	    data &= 0x0f;
	    efuse_write_key_byte(&s_efuse_obj, 63, (u8)data);

		/*
		 * bit0: lock block0 ~ block3
		 * bit1: lock block4 ~ block7
		 * bit2: lock block8 ~ block11
		 */
		data = status->efuse_apb_lock;
	    data &= 0x7;
		efuse_write_key_byte(&s_efuse_obj, 60, (u8)data);
	}
}

static void efuse_set_write_lock(struct wrap_efuse_obj *obj,
	struct efuse_status *status)
{
	u32 write_lock_data_V64[2] = {0};
	u8 write_lock_data_V16[2] = {0};
	
	if (obj->total_blocks == EFUSE_64BLK_VER) {
		memcpy(write_lock_data_V64,
			status->efuse_write_lock,
			sizeof(write_lock_data_V64));
		/* write_lock_data[0]~[1]: total 32+25=57 max@blk56 */
	    write_lock_data_V64[1] &= 0x01ffffff;
	    efuse_write_entry(&s_efuse_obj, 244,
			(u8 *)write_lock_data_V64,
			sizeof(write_lock_data_V64));
	} else {
		memcpy(write_lock_data_V16,
			status->efuse_write_lock,
			sizeof(write_lock_data_V16));
		/* write_lock_data[0]~[1]: total 8+4=12 max@blk11 */
		write_lock_data_V16[1] &= 0x0f;
		efuse_write_entry(&s_efuse_obj, 61,
			write_lock_data_V16,
			sizeof(write_lock_data_V16));
	}
}

static void efuse_load_usrcmd(struct wrap_efuse_obj *obj)
{
	wrap_writel(obj, efuse_cmd, CMD_LOAD_USR_CMD);
	efuse_detect_complete(obj, 1);
}

static void refresh_efuse(struct wrap_efuse_obj *obj)
{
	wrap_writel(obj, efuse_cmd, CMD_WFLGA_AUTO);
	efuse_detect_complete(obj, 8);
	efuse_load_usrcmd(obj);
}

static void aes_biglittle_swap(u8 *buf)
{
	u8 tmp, tmp1;

	tmp = buf[0];
	tmp1 = buf[1];
	buf[0] = buf[3];
	buf[1] = buf[2];
	buf[2] = tmp1;
	buf[3] = tmp;
}

long mol_efuse_ioctl(struct file *filp, unsigned int cmd,
		unsigned long arg)
{
	EFUSE_INFO efuse_info = {0};
	u32 i;
    u32 bit_pos;
    u32 bit_val;
	u32 temp_swap_data[64] = {0};

	if (copy_from_user((void *) &efuse_info,
		(void __user *) arg,
		sizeof(EFUSE_INFO)))
		return -EFAULT;

	refresh_efuse(&s_efuse_obj);
	switch (cmd) {
	case IOCTL_EFUSE_WRITE_ENTRY:
		if (copy_from_user((void *) &temp_swap_data[0],
			(void __user *) efuse_info.key_buff,
			efuse_info.key_size))
			return -EFAULT;

		efuse_write_entry(&s_efuse_obj,
			efuse_info.efuse_entry_no,
			(u8 *)temp_swap_data,
			efuse_info.key_size);
		break;
	case IOCTL_EFUSE_READ_ENTRY:
		efuse_read_entry(&s_efuse_obj,
			efuse_info.status.error,
			efuse_info.efuse_entry_no,
			(u8 *)&temp_swap_data[0],
			efuse_info.key_size);

		if (copy_to_user((void __user *) (efuse_info.key_buff),
			(void *) &temp_swap_data[0],
			efuse_info.key_size))
			return -EFAULT;
		break;
	case IOCTL_EFUSE_WRITE_KEY:
		if (copy_from_user((void *) &temp_swap_data[0],
			(void __user *) efuse_info.key_buff,
			efuse_info.key_size))
			return -EFAULT;

		for (i = 0; i < efuse_info.key_size / sizeof(u32); i++)
			aes_biglittle_swap((u8 *)(temp_swap_data + i));

		efuse_write_entry(&s_efuse_obj,
			efuse_info.efuse_entry_no,
			(u8 *)temp_swap_data,
			efuse_info.key_size);
		break;
	case IOCTL_EFUSE_READ_KEY:
		efuse_read_entry(&s_efuse_obj,
			efuse_info.status.error,
			efuse_info.efuse_entry_no,
			(u8 *)&temp_swap_data[0],
			efuse_info.key_size);

		for (i = 0; i < efuse_info.key_size / sizeof(u32); i++)
			aes_biglittle_swap((u8 *)(temp_swap_data + i));

		if (copy_to_user((void __user *) (efuse_info.key_buff),
			(void *) &temp_swap_data[0],
			efuse_info.key_size))
			return -EFAULT;
		break;
	case IOCTL_EFUSE_WRITE_BIT:
        bit_pos = efuse_info.bit_pos;
        bit_val = efuse_info.bit_val;
        if (bit_val == 0) {
            pr_err("only could set bit to '1'..\n");
            break;
        }
        if (bit_pos > 7) {
            pr_err("pos from '0' to '7', bit_pos[%d] err..\n", bit_pos);
            break;
        }
        efuse_write_key_bit_to_1(&s_efuse_obj, efuse_info.efuse_entry_no, bit_pos);
        break;
	case IOCTL_EFUSE_CHECK_PRO:
		auto_check_efuse_pro_bits(&s_efuse_obj, &efuse_info.status);
		break;
	case IOCTL_EFUSE_CHECK_LOCK:
		efuse_get_lock_status(&s_efuse_obj, &efuse_info.status);
		break;
	case IOCTL_EFUSE_SET_LOCK_WRITE_DATA:
		efuse_set_write_lock(&s_efuse_obj, &efuse_info.status);
        break;
	case IOCTL_EFUSE_SET_LOCK:
		efuse_set_read_lock(&s_efuse_obj, &efuse_info.status);
		break;
	case IOCTL_EFUSE_TRANS_KEY:
		printk(KERN_WARNING "please use efuse transkey with aes...\n");
		break;
	case IOCTL_EFUSE_SWITCH_CPU_KEY_MODE:
		printk(KERN_WARNING "please use cpu key with aes...\n");
		break;
	case IOCTL_EFUSE_SWITCH_EFUSE_KEY_MODE:
		printk(KERN_WARNING "please use efuse key with aes...\n");
		break;
	case IOCTL_EFUSE_CHECK_ERROR:
		printk(KERN_WARNING "not support...\n");
		break;
	case IOCTL_EFUSE_SET_MAP_PARA_4_TO_1:
		printk(KERN_WARNING "not support here..\
		pls check if chip support this func and set core para\n");
		break;
	case IOCTL_EFUSE_SET_MAP_PARA_1_TO_1:
		printk(KERN_WARNING "not support func here...\n");
		break;
	case IOCTL_EFUSE_CLR_MAP_PARA:
		printk(KERN_WARNING "not support here...\n");
		break;
	case IOCTL_EFUSE_MEM_CMP:
		printk(KERN_WARNING "not support here...\n");
		break;
	default:
		break;
	}
	memcpy((void *)&s_efuse_obj.status, (void *)&efuse_info.status,
			sizeof(struct efuse_status));
	if (copy_to_user((void __user *) (&((EFUSE_INFO *)arg)->status),
			(void *) &efuse_info.status,
			sizeof(struct efuse_status))) {
			return -EFAULT;
	}
	return 0;
}



void efuse_check_map_para(unsigned int size, struct ex_key_map *p_key_map)
{
	int loop;

	if (size > MAX_EX_KEY_MAP_SIZE) {
		printk(KERN_ERR "%s :: size : %d > max size : %d\n",
		__func__, size, MAX_EX_KEY_MAP_SIZE);
	}

	for (loop = 0; loop < size; loop++) {
		if ((p_key_map[loop].ex_mem_entry % 4 != 0)
			|| (p_key_map[loop].crypto_key_no != loop)) {
				printk(KERN_ERR \
				"map[%d]:entry[0x%x]:aes key[0x%x] para error..\n",
				loop,
				p_key_map[loop].ex_mem_entry,
				p_key_map[loop].crypto_key_no);
		}
	}
}

static int get_soc_crypto_load_mode_from_efuse(struct wrap_efuse_obj *p_efuse)
{
	unsigned char ret_buf;
	u32 sec_boot_entry;

	if (p_efuse->total_blocks == EFUSE_64BLK_VER)
		sec_boot_entry = SECURE_BOOT_ENTRY_V64;
	else
		sec_boot_entry = SECURE_BOOT_ENTRY_V16;

	refresh_efuse(p_efuse);
	efuse_read_entry(p_efuse, MATCH_KEY,
		sec_boot_entry, &ret_buf, 1);

    if (ret_buf & SECURE_BOOT_ACTIVE_VAL)
		return SOC_BOOT_MODE_WITH_SECURE;

    return -1;
}

/*
 * 0      :means do not use secure boot or decrypt. all efuse could be transfered to aes
 * others :means use secure boot mode. efuse entry 0~15 DO NOT USE
 */
int mol_efuse_secure_check(struct wrap_efuse_obj *obj,
	u32 start_no, u32 size, struct af_alg_usr_def *p_alg)
{
	int ret;
	int i;
	int key_map_size;
	struct ex_key_map *p_key_map;

	ret = get_soc_crypto_load_mode_from_efuse(obj);
    if (ret != SOC_BOOT_MODE_WITH_SECURE)
		return 0;

	if (obj->open_flag & CRYPTO_EX_MEM_SWITCH_KEY) {
		if (p_alg->mode & CRYPTO_EX_MEM_SWITCH_KEY) {
			if (p_alg->mode & CRYPTO_EX_MEM_4_ENTRY_1_KEY) {
				key_map_size = p_alg->adv.ex_key_para.map_size;
				p_key_map = &p_alg->adv.ex_key_para.map[0];
				/* check para. */
				for (i = 0; i < key_map_size; i++, p_key_map++) {
					if (p_key_map->ex_mem_entry <
					SECURE_PROTECT_EFUSE_ENTRY_MAX_INDEX) {
						pr_err("Do not use secure area\n");
						return -1;
					}
				}
			}
		} else {
			/*efuse 0 ~ 15 must be used*/
			return -2;
		}
	} else {
		/* efuse 0 ~ 15 must be used*/
		return -3;
	}
	return 0;
}

void efuse_check_update_trans_flag(struct wrap_efuse_obj *obj,
	u32 start_no, u32 size, struct af_alg_usr_def *p_alg){

	int i;
	struct ex_key_map *p_key_map;
	struct ex_key_map *old_p_key_map;
	struct af_alg_usr_def *p_old_usr_def;
	int key_map_size;

	p_old_usr_def = &obj->old_usr_def;
	/* first check if use cpu mode */
	if (p_old_usr_def->mode != p_alg->mode) {
		obj->efuse_trans_flag = EFUSE_NEED_TRANS;
		p_old_usr_def->mode = p_alg->mode;
		/* not return here..maybe para not the same..
		just update below. */
	}
	if (obj->open_flag & CRYPTO_EX_MEM_SWITCH_KEY) {
		if (p_alg->mode & CRYPTO_EX_MEM_SWITCH_KEY) {
			if (p_alg->mode & CRYPTO_EX_MEM_4_ENTRY_1_KEY) {
				key_map_size = p_alg->adv.ex_key_para.map_size;
				p_key_map = &p_alg->adv.ex_key_para.map[0];
				old_p_key_map = &p_old_usr_def->adv.ex_key_para.map[0];
				efuse_check_map_para(key_map_size, p_key_map);
				/* check map size */
				if (key_map_size != p_old_usr_def->adv.ex_key_para.map_size) {
					/* cpy new to old */
					memcpy(p_old_usr_def, p_alg,
						sizeof(struct af_alg_usr_def));
					obj->efuse_trans_flag = EFUSE_NEED_TRANS;
					return;
				}
				/* check para. */
				for (i = 0; i < key_map_size; i++,
					p_key_map++, old_p_key_map++) {
					if (memcmp(p_key_map, old_p_key_map,
						sizeof(struct ex_key_map))) {
						/* cmp error */
						memcpy(p_old_usr_def, p_alg,
						sizeof(struct af_alg_usr_def));
						obj->efuse_trans_flag = EFUSE_NEED_TRANS;
						return;
					}
				}
			}
		} else {
			/*chip need set the map...and usr not set */
			key_map_size = size;
			if (key_map_size != obj->old_size) {
				obj->old_size = key_map_size;
				obj->efuse_trans_flag = EFUSE_NEED_TRANS;
				return;
			}
		}
	} else {
		key_map_size = size;
		if (key_map_size != obj->old_size) {
			obj->old_size = key_map_size;
			obj->efuse_trans_flag = EFUSE_NEED_TRANS;
			return;
		}
	}
}

void efuse_trans_key(struct wrap_efuse_obj *obj,
	u32 start_no, u32 size, struct af_alg_usr_def *p_alg)
{
	int i;
	struct ex_key_map *p_key_map;
	int key_map_size;
	unsigned int temp_reg;

	efuse_check_update_trans_flag(obj, start_no, size, p_alg);
	if (obj->efuse_trans_flag != EFUSE_NEED_TRANS) {
		/*pr_err("###DO NOT need to update efuse para to aes...\n");*/
		return;
	}
	obj->efuse_trans_flag = 0;

	if (obj->open_flag & CRYPTO_EX_MEM_SWITCH_KEY) {
		if (p_alg->mode & CRYPTO_EX_MEM_SWITCH_KEY) {
			if (p_alg->mode & CRYPTO_EX_MEM_4_ENTRY_1_KEY) {
				key_map_size = p_alg->adv.ex_key_para.map_size;
				p_key_map = &p_alg->adv.ex_key_para.map[0];
				efuse_check_map_para(key_map_size, p_key_map);
				for (i = 0; i < key_map_size;
					i++, p_key_map++) {
					refresh_efuse(obj);
					temp_reg =
					wrap_readl(obj, efuse_config);
					temp_reg &= ~(0xff << 24);
					temp_reg |=
					(p_key_map->ex_mem_entry / 4) << 24;
					wrap_writel(obj,
					efuse_config, temp_reg);
					wrap_writel(obj,
					efuse_cmd, (i << 20) + 0x04);
					efuse_detect_complete(obj, 4);
				}
		}
		} else {
			/*chip need set the map...and usr not set */
			key_map_size = size;
			for (i = 0; i < key_map_size; i++) {
				refresh_efuse(obj);
				temp_reg = wrap_readl(obj, efuse_config);
				temp_reg &= ~(0xff << 24);
				temp_reg |= i << 24;
				wrap_writel(obj, efuse_config, temp_reg);
				wrap_writel(obj, efuse_cmd, (i << 20) + 0x04);
				efuse_detect_complete(obj, 4);
			}
		}
	} else {
		key_map_size = size;
		for (i = 0; i < key_map_size; i++) {
			refresh_efuse(obj);
			wrap_writel(obj, efuse_cmd, (i << 20) + 0x04);
			efuse_detect_complete(obj, 4);
		}
	}
}

void efuse_release_key(void)
{
	struct wrap_efuse_obj *obj = &s_efuse_obj;

	refresh_efuse(obj);
	wrap_writel(obj, efuse_cmd, 9);
	efuse_detect_complete(obj, 15);
}

bool ades_use_efuse_key(u32 key_size, u32 mode, u32 map_size, u32 map[][2], u32 is_enc)
{
	if (mode & CRYPTO_EX_MEM_SET_KEY) {
		struct af_alg_usr_def usr_def;
		int i;

		s_efuse_obj.trans_key_start_no = 0;
		s_efuse_obj.trans_key_size = key_size / 4;

		usr_def.mode = mode;
		usr_def.adv.ex_key_para.map_size = map_size;
		for (i=0; i<map_size; i++) {
			usr_def.adv.ex_key_para.map[i].crypto_key_no = map[i][0];
			usr_def.adv.ex_key_para.map[i].ex_mem_entry = map[i][1];
		}

		if (is_enc &&
			mol_efuse_secure_check(&s_efuse_obj,
			s_efuse_obj.trans_key_start_no,
			s_efuse_obj.trans_key_size, &usr_def)) {
			pr_err("secure check failed..\n");
		} else {
			efuse_trans_key(&s_efuse_obj,
				s_efuse_obj.trans_key_start_no,
				s_efuse_obj.trans_key_size, &usr_def);
		}
		
		return true;
	} else {
		s_efuse_obj.old_usr_def.mode &= ~CRYPTO_EX_MEM_SET_KEY;
		s_efuse_obj.old_usr_def.mode |= CRYPTO_CPU_SET_KEY;
		efuse_release_key();

		return false;
	}
}
EXPORT_SYMBOL(ades_use_efuse_key);



static void mol_efuse_unlock_all_blk(void){
	//TBD :: BLK 40 ~ 47 will not support write
}

static void open_efuse_power(struct wrap_efuse_obj *obj)
{
	u32 data = 0;

	data = wrap_readl(obj, efuse_config);
	data |= 1<<18;
	wrap_writel(obj, efuse_config, data);
}

static void efuse_transfer_to_aes_width_set(struct wrap_efuse_obj *obj, unsigned int value)
{
    unsigned int data;
    data = wrap_readl(obj, efuse_config);
    data &= ~(0x1f << 19);
    data |= (value << 19);
    wrap_writel(obj, efuse_config, data);
}

/*****************************************************************************
 *
 *
 *		function body
 *
 *
 *****************************************************************************/
static int mol_efuse_probe(struct platform_device *pdev)
{
	struct resource *res;
	int err = -ENODEV;
	struct device_node *np = pdev->dev.of_node;
	int temp;
	struct mol_efuse_platform_data *p_efuse_plat;
	pr_err("!!efuse probe get in...\n");
	if (np && !IS_ERR(np)) {
		s_efuse_obj.open_flag = CRYPTO_CPU_SET_KEY |
		CRYPTO_EX_MEM_SET_KEY;
		s_efuse_obj.regs = of_iomap(np, 0);
		if (!s_efuse_obj.regs) {
			dev_err(&pdev->dev, "efuse io map error..\n");
			return -EINVAL;
		}
		if (strcmp("enable",
		of_get_property(np, "key_switch", &temp)) == 0)
			s_efuse_obj.open_flag |=
			CRYPTO_EX_MEM_SWITCH_KEY | CRYPTO_EX_MEM_4_ENTRY_1_KEY;
		if (strcmp("enable",
		of_get_property(np, "indep_power", &temp)) == 0)
			s_efuse_obj.open_flag |=
			CRYPTO_EX_MEM_INDEP_POWER;
	} else {
		res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
		if (!res) {
			dev_err(&pdev->dev, "can't fetch device resource info\n");
			return -EINVAL;
		}

		s_efuse_obj.regs = ioremap(res->start, resource_size(res));
		if (s_efuse_obj.regs == NULL) {
			dev_err(&pdev->dev, "ioremap resource error\n");
			return -EINVAL;
		}

		p_efuse_plat =
		(struct mol_efuse_platform_data *) pdev->dev.platform_data;
		if (p_efuse_plat == NULL) {
			dev_err(&pdev->dev, "efuse get platform data error..\n");
			return -ENODEV;
		}
		s_efuse_obj.open_flag = p_efuse_plat->efuse_support_flag;
		s_efuse_obj.total_blocks = p_efuse_plat->block_version;
	}

#if(0)
//TBD
	s_efuse_obj.clk = clk_get(&pdev->dev, "efuse_clk");

	if (IS_ERR(s_efuse_obj.clk)) {
		err = PTR_ERR(s_efuse_obj.clk);
		return err;
	}
	clk_prepare_enable(s_efuse_obj.clk);
#endif
	pr_info("efuse open mode is %x\n", s_efuse_obj.open_flag);
	if (s_efuse_obj.open_flag & CRYPTO_EX_MEM_INDEP_POWER) {
		/*efuse power up*/
		open_efuse_power(&s_efuse_obj);
	}

	efuse_transfer_to_aes_width_set(&s_efuse_obj, EFUSE_TRANS_TO_AES_WIDTH_32);
	mol_efuse_unlock_all_blk();
	err = misc_register(&mol_efuse_misc);
	if (err != 0) {
		dev_err(&pdev->dev, "efuse register misc error\n");
		return err;
	}

	platform_set_drvdata(pdev, &mol_efuse_misc);

#if(0)
	//add self test map func..
	struct af_alg_usr_def test_usr = {0};
	//test_usr.mode =  CRYPTO_EX_MEM_SWITCH_KEY;
	test_usr.mode = CRYPTO_EX_MEM_SWITCH_KEY | CRYPTO_EX_MEM_4_ENTRY_1_KEY;

	test_usr.adv.ex_key_para.map_size = 4;
	test_usr.adv.ex_key_para.map[0].crypto_key_no = 0;
	test_usr.adv.ex_key_para.map[0].ex_mem_entry = 0;

	test_usr.adv.ex_key_para.map[1].crypto_key_no = 1;
	test_usr.adv.ex_key_para.map[1].ex_mem_entry = 8;

	test_usr.adv.ex_key_para.map[2].crypto_key_no = 2;
	test_usr.adv.ex_key_para.map[2].ex_mem_entry = 4;

	test_usr.adv.ex_key_para.map[3].crypto_key_no = 3;
	test_usr.adv.ex_key_para.map[3].ex_mem_entry = 12;

	efuse_trans_key(&s_efuse_obj, 0, 4, &test_usr);
#endif

	return 0;

}

static int mol_efuse_remove(struct platform_device *pdev)
{
	struct miscdevice *misc;

	misc = (struct miscdevice *)platform_get_drvdata(pdev);
	misc_deregister(misc);

	return 0;
}

#ifdef CONFIG_PM
static int efuse_mol_runtime_suspend(struct device *dev)
{
	return 0;
}

static int efuse_mol_runtime_resume(struct device *dev)
{
	return 0;
}

UNIVERSAL_DEV_PM_OPS(efuse_mol_pm_ops, efuse_mol_runtime_suspend,
		   efuse_mol_runtime_resume, NULL);

#define EFUSE_MOL_PM_OPS (&efuse_mol_pm_ops)
#else
#define EFUSE_MOL_PM_OPS NULL
#endif /* CONFIG_PM */

#ifdef CONFIG_OF
static const struct of_device_id mol_efuse_of_match[] = {
	{.compatible = "fh,fh-efuse-v2",},
	{},
};

MODULE_DEVICE_TABLE(of, mol_efuse_of_match);
#endif

static struct platform_driver mol_efuse_driver = {
	.driver = {
		.name = "fh_efuse",
		.pm = EFUSE_MOL_PM_OPS,
#ifdef CONFIG_OF
		.of_match_table = mol_efuse_of_match,
#endif
	},
	.probe = mol_efuse_probe,
	.remove = mol_efuse_remove,
};

module_platform_driver(mol_efuse_driver);
MODULE_DESCRIPTION("mol efuse device driver");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("zhangy@fullhan.com");
MODULE_ALIAS("platform:mol_efuse");
